﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoA.CameraEngine.Entities;
using LoA.Shared;
using LoA.Shared.Interfaces;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace LoA.CameraEngine.Entities {
	internal sealed class FreeCameraProcessor : CameraProcessorBase {
		private Matrix cameraMatrix;
		private Vector3 cameraPosition;

		public FreeCameraProcessor(Game game) {
			this.FieldOfView = 0.85f;
			this.NearPlaneDistance = 0.01f;
			this.FarPlaneDistance = 1000.0f;

			this.cameraMatrix = Matrix.Identity;

			this.CameraPosition = new Vector3(10f, 2.5f, 10f);
			this.TargetPosition = Vector3.UnitX * 3 + Vector3.UnitZ;
			this.CameraUp = Vector3.Up;

			this.UpdateViewMatrix( );
		}

		public Matrix CameraMatrix {
			get { return this.cameraMatrix; }
			set {
				this.cameraMatrix = value;
				this.UpdateViewMatrix( );
			}
		}
		
		public Vector3 CameraPosition {
			get { return this.cameraPosition; }
			private set {
				this.cameraPosition = value;
				this.UpdateViewMatrix( );
			}
		}

		public Vector3 TargetPosition {
			get { return this.CameraPosition + this.CameraMatrix.Forward; }
			private set {
				var forward = this.CameraPosition - value;
				forward.Normalize( );
				this.cameraMatrix.Forward = forward;
				this.UpdateViewMatrix( );
			}
		}

		public Vector3 CameraUp {
			get { return this.CameraMatrix.Up; }
			private set {
				var up = value;
				up.Normalize( );
				this.cameraMatrix.Up = up;
				this.UpdateViewMatrix( );
			}
		}

		private void UpdateViewMatrix( ) {
			this.ViewMatrix = Matrix.CreateLookAt(this.CameraPosition, this.TargetPosition, this.CameraUp);
		}

		public override void Update(GameTime gameTime, IInput inputEngine) {

			IInputManager inputManager = inputEngine.GetInputManager(PlayerIndex.One);

			if (inputManager.KeyboardState.IsKeyDown(Keys.NumPad8) || inputManager.KeyboardState.IsKeyDown(Keys.NumPad9))
				this.Move(Direction3d.Forward, 1.5f);
			if (inputManager.KeyboardState.IsKeyDown(Keys.NumPad2) || inputManager.KeyboardState.IsKeyDown(Keys.NumPad3))
				this.Move(Direction3d.Backward, 1f);
			if (inputManager.KeyboardState.IsKeyDown(Keys.NumPad4))
				this.Turn(Rotation3d.TurnLeft, MathHelper.PiOver2);
			if (inputManager.KeyboardState.IsKeyDown(Keys.NumPad6))
				this.Turn(Rotation3d.TurnRight, MathHelper.PiOver2);
			if (inputManager.KeyboardState.IsKeyDown(Keys.NumPad7))
				this.Turn(Rotation3d.TurnUp, MathHelper.PiOver2);
			if (inputManager.KeyboardState.IsKeyDown(Keys.NumPad1))
				this.Turn(Rotation3d.TurnDown, MathHelper.PiOver2);

			this.UpdateViewMatrix( );
		}

		public void Move(Direction3d direction3d, float p) {
			Vector3 position = Vector3.Zero;
			if ((direction3d & Direction3d.Forward) == Direction3d.Forward) {
				position += this.CameraMatrix.Forward * p;
			}
			if ((direction3d & Direction3d.Backward) == Direction3d.Backward) {
				position += this.CameraMatrix.Backward * p;
			}
			if ((direction3d & Direction3d.Left) == Direction3d.Left) {
				position += this.CameraMatrix.Left * p;
			}
			if ((direction3d & Direction3d.Right) == Direction3d.Right) {
				position += this.CameraMatrix.Right * p;
			}
			if ((direction3d & Direction3d.Up) == Direction3d.Up) {
				position += this.CameraMatrix.Up * p;
			}
			if ((direction3d & Direction3d.Down) == Direction3d.Down) {
				position += this.CameraMatrix.Down * p;
			}
			this.CameraPosition += position;
			this.UpdateViewMatrix( );
		}

		public void Turn(Rotation3d rotation3d, float p) {
			if ((rotation3d & Rotation3d.PitchUp) == Rotation3d.PitchUp) {
				Vector2 v = new Vector2(this.CameraMatrix.Forward.X, this.CameraMatrix.Forward.Z);
				v.Normalize( );
				this.CameraMatrix = Matrix.Transform(this.CameraMatrix, Quaternion.CreateFromAxisAngle(new Vector3(-v.Y, 0, v.X), MathHelper.PiOver4 / 20 * p));
			}
			if ((rotation3d & Rotation3d.PitchDown) == Rotation3d.PitchDown) {
				Vector2 v = new Vector2(this.CameraMatrix.Forward.X, this.CameraMatrix.Forward.Z);
				v.Normalize( );
				this.CameraMatrix = Matrix.Transform(this.CameraMatrix, Quaternion.CreateFromAxisAngle(new Vector3(-v.Y, 0, v.X), MathHelper.PiOver4 / 20 * -p));
			}
			if ((rotation3d & Rotation3d.YawLeft) == Rotation3d.YawLeft) {
				this.CameraMatrix = Matrix.Transform(this.CameraMatrix, Quaternion.CreateFromYawPitchRoll(MathHelper.PiOver4 / 20 * p, 0, 0));
			}
			if ((rotation3d & Rotation3d.YawRight) == Rotation3d.YawRight) {
				this.CameraMatrix = Matrix.Transform(this.CameraMatrix, Quaternion.CreateFromYawPitchRoll(MathHelper.PiOver4 / 20 * -p, 0, 0));
			}
			if ((rotation3d & Rotation3d.RollLeft) == Rotation3d.RollLeft) {
				this.CameraMatrix = Matrix.Transform(this.CameraMatrix, Quaternion.CreateFromAxisAngle(new Vector3(this.CameraMatrix.Forward.X, 0, this.CameraMatrix.Forward.Z), MathHelper.PiOver4 / 20 * p));
			}
			if ((rotation3d & Rotation3d.RollRight) == Rotation3d.RollRight) {
				this.CameraMatrix = Matrix.Transform(this.CameraMatrix, Quaternion.CreateFromAxisAngle(new Vector3(this.CameraMatrix.Forward.X, 0, this.CameraMatrix.Forward.Z), MathHelper.PiOver4 / 20 * -p));
			}
			this.UpdateViewMatrix( );
		}
	}
}
